#import "BWSymbol.h"

@implementation BWSymbol

NSString *BWSymbol_PackageNameType = @"BWSymbol Package Name Type";
NSString *BWSymbol_SymbolIdType = @"BWSymbol Symbol Id Type";

- (id) init
{
    if (self = [super init]) {
        itemCount = 0;
        items = malloc (sizeof(BWSymbolItem) * 0);
        ghostIndex = -1;
        name = @"";
        keywords = @"";
    }
    
    return (self);

} // init


- (void) dealloc
{
    [keywords release];
    [name release];
    free (items);

    [super dealloc];

} // dealloc


- (int) pointCount
{
    int count = 0;
    int i;

    for (i = 0; i < itemCount; i++) {
        switch (items[i].type) {
        case NSMoveToBezierPathElement:
        case NSLineToBezierPathElement:
            count++;
            break;
            
        case NSCurveToBezierPathElement:
            count += 3;
            break;
            
        default:
            NSLog (@"unexpected type in pointCount");
            break;
        }
    }

    return (count);

} // pointCount



// pessimalness!!!

- (NSPoint) pointAtIndex: (int) index
{
    int i, pointCount = 0;
    NSPoint point = NSZeroPoint;

    if (ghostIndex == index) {
        point = ghostPoint;
        goto done;
    }

    for (i = 0; i < itemCount; i++) {
        if ((ghostIndex == pointCount)
            && (pointCount == index)) {
            point = ghostPoint;
            goto done;
        }
        switch (items[i].type) {

        case NSMoveToBezierPathElement:
        case NSLineToBezierPathElement:

            if (pointCount == index) {
                point = items[i].points[0];
                goto done;
            } else {
                pointCount++;
            }
            break;

        case NSCurveToBezierPathElement:

            if ((index >= pointCount)
                && (index < pointCount + 3)) {
                // we got it.

                int offset;
                offset = index - pointCount;
                if (offset < 0 || offset >= 3) {
                    NSLog (@"oops!  pointAtIndex math is wrong");
                }
                point = items[i].points[offset];
                goto done;
            } else {
                pointCount += 3;
            }
            break;

        default:
            NSLog (@"unexpected element type at pointAtIndex");
            break;
        }
    }
    NSLog (@"oops! fell off end of loop without finding point. in pointAtIndex");
done:
    return (point);

} // pointAtIndex


- (void) addElement: (NSBezierPathElement) element
            atIndex: (int) index
   associatedPoints: (NSPoint *) points
{
    if ((element == NSCurveToBezierPathElement)
        && (itemCount == 0)) {
        NSPoint origin;
        origin = NSZeroPoint;
        [self addElement: NSMoveToBezierPathElement
              atIndex: 0
              associatedPoints: &origin];
        // this will bump up itemCount

        index++; // we're now inserting after the first item
    }

    if (index < itemCount) {
        // open space
        memmove (items + index + 1, items + index,
                 sizeof(BWSymbolItem) * (itemCount - index));
    }

    items = realloc (items, 
                     sizeof(BWSymbolItem) * (itemCount + 1));
    
    items[index].points[0] = NSZeroPoint;
    items[index].points[1] = NSZeroPoint;
    items[index].points[2] = NSZeroPoint;
    
    switch (element) {
    case NSMoveToBezierPathElement:
    case NSLineToBezierPathElement:
        items[index].points[0] = points[0];
        if (itemCount == 0) {
            // force the first item to be a move to
            element = NSMoveToBezierPathElement;
        }
        break;
        
    case NSCurveToBezierPathElement:
        items[index].points[0] = points[0];
        items[index].points[1] = points[1];
        items[index].points[2] = points[2];
        break;

    default:
        NSLog (@"unexpected element type in addElements:associatedPoints: %d",
               element);
        break;
    }
    items[index].type = element;
    itemCount++;

} // addElement



- (int) elementCount
{
    return (itemCount);

} // elementCount



- (NSBezierPathElement) elementAtIndex: (int) index // 0 .. elementCount
                      associatedPoints: (NSPoint *) points
{
    NSBezierPathElement element = -1;
    int ghostOffset = -1;

    if (index >= itemCount) {
        NSLog (@"oops - out of bounds index at elementAtIndex in BWSymbol");
    } else {

        // see if this is where our ghost point lives
        int i;
        int pointCount = 0;
        for (i = 0; i <= index; i++) {
            element = items[i].type;

            switch (element) {
            case NSMoveToBezierPathElement:
            case NSLineToBezierPathElement:
                if (pointCount == ghostIndex) {
                    ghostOffset = 0;
                    goto done;
                }
                pointCount++;
                break;
                
            case NSCurveToBezierPathElement:
                if ((ghostIndex >= pointCount)
                    && (ghostIndex < pointCount + 3)) {

                    ghostOffset = ghostIndex - pointCount;

                    if ((ghostOffset < 0)
                        || (ghostOffset >= 3)) {
                        NSLog (@"oops!  elementAtIndex math is wrong");
                    }

                    goto done;
                } else {
                    pointCount += 3;
                }
                break;

            default:
                NSLog (@"unexpected type in elementAtIndex");
                break;
            }
        }
    done:
        if (i != index) {
            ghostOffset = -1;
        }
        
        // in case we're ghosting an earlier point

        element = items[index].type;

        if (points != NULL) {
            switch (element) {
            case NSMoveToBezierPathElement:
            case NSLineToBezierPathElement:
                points[0] = items[index].points[0];
                break;
                
            case NSCurveToBezierPathElement:
                points[0] = items[index].points[0];
                points[1] = items[index].points[1];
                points[2] = items[index].points[2];
                break;
                
            default:
                NSLog (@"unexpected type in elementAtIndex");
                break;
            }
            if (ghostOffset != -1) {
                points[ghostOffset] = ghostPoint;
            }
        }
    }

    return (element);

} // elementAtIndex


- (void) setGhostIndex: (int) index  point: (NSPoint) point
{
    ghostIndex = index;
    ghostPoint = point;

} // setGhostIndex 



- (void) setPoint: (NSPoint) point  atIndex: (int) index
{
    int i, pointCount = 0;

    for (i = 0; i < itemCount; i++) {
        switch (items[i].type) {
        case NSMoveToBezierPathElement:
        case NSLineToBezierPathElement:
            if (pointCount == index) {
                items[i].points[0] = point;
                goto done;
            } else {
                pointCount++;
            }
            break;

        case NSCurveToBezierPathElement:
            if ((index >= pointCount)
                && (index < pointCount + 3)) {
                // we got it.

                int offset;
                offset = index - pointCount;
                if (offset < 0 || offset >= 3) {
                    NSLog (@"oops!  setpoint math is wrong");
                }
                items[i].points[offset] = point;
                goto done;
            } else {
                pointCount += 3;
            }
            break;

        default:
            NSLog (@"unexpected element type at setPoint");
            break;
        }
    }
done:
    return;

} // setPoint


- (int) elementIndexForPointIndex: (int) index
{
    int i;
    int pointCount = 0;

    for (i = 0; i < itemCount; i++) {
        switch (items[i].type) {
        case NSMoveToBezierPathElement:
        case NSLineToBezierPathElement:
            if (pointCount == index) {
                goto bailout;
            }
            pointCount++;
            break;
            
        case NSCurveToBezierPathElement:
            if ((index >= pointCount)
                && (index < pointCount + 3)) {
                goto bailout;
            }
            pointCount += 3;
            break;
            
        default:
            NSLog (@"unexpected type in pointCount");
            break;
        }
    }
bailout:
    return (i);

} // elementIndexForPointIndex


- (void) removeElementIndex: (int) index
{
    itemCount--;

    if (index < itemCount) {
        memmove (items + index, items + index + 1,
                 sizeof(BWSymbolItem) * (itemCount - index));
    }
    
} // removeElementIndex


- (void) encodeWithCoder: (NSCoder *) aCoder
{
    [aCoder encodeValueOfObjCType: @encode(int)
            at: &itemCount];

    int i;
    for (i = 0; i < itemCount; i++) {
        [aCoder encodeValueOfObjCType: @encode(BWSymbolItem)
                at: &items[i]];
    }
    
    [aCoder encodeObject: name];
    [aCoder encodeObject: keywords];

    [aCoder encodeObject: packageName];
    [aCoder encodeValueOfObjCType: @encode(int)
            at: &symbolId];

} // encodeWithCoder


- (id) initWithCoder: (NSCoder *) aDecoder
{
    if (self = [super init]) {
        [aDecoder decodeValueOfObjCType: @encode(int)
                  at: &itemCount];
        
        if (items != NULL) {
            free (items);
        }
        items = malloc (sizeof(BWSymbolItem) * itemCount);
        
        int i;
        for (i = 0; i < itemCount; i++) {
            [aDecoder decodeValueOfObjCType: @encode(BWSymbolItem)
                      at: &items[i]];
        }
        
        name = [aDecoder decodeObject];
        [name retain];

        keywords = [aDecoder decodeObject];
        if (keywords == nil) {
            NSLog (@"nil keywords");
            keywords = @"";
        }
        [keywords retain];

        // other random initializations
        ghostIndex = -1;

        packageName = [aDecoder decodeObject];
        [aDecoder decodeValueOfObjCType: @encode(int)
                  at: &symbolId];
        [packageName retain];
    }

    return (self);

} // initWithCoder


- (NSString *) name
{
    return (name);
} // name


- (void) setName: (NSString *) newName
{
    [name release];
    name = [newName copy];

} // setName


- (NSString *) keywords
{
    return (keywords);
} // keywords


- (void) setKeywords: (NSString *) kw
{
    [keywords release];
    keywords = [kw copy];
} //  setKeywords



NSPoint scalePoint (NSPoint point, float amount, NSPoint origin)
{
    point.x *= amount;
    point.y *= amount;
    point.x += origin.x;
    point.y += origin.y;

    return (point);

} // scalePoint



- (NSBezierPath *) pathInRect: (NSRect) rect
{
    NSBezierPath *path;
    NSPoint points[3];

    path = [NSBezierPath bezierPath];

    float size = rect.size.width;
    
    int i;
    for (i = 0; i < itemCount; i++) {
        NSBezierPathElement type;
        // make sure we get the ghost point
        type = [self elementAtIndex: i
                     associatedPoints: points];

        switch (type) {
        case NSMoveToBezierPathElement:
            [path moveToPoint: 
                      scalePoint (points[0], size, rect.origin)];
            break;
            
        case NSLineToBezierPathElement:
            [path lineToPoint: 
                      scalePoint (points[0], size, rect.origin)];
            break;
            
        case NSCurveToBezierPathElement:
            scalePoint (points[0], size, rect.origin);
            scalePoint (points[1], size, rect.origin);
            scalePoint (points[2], size, rect.origin);
            
            [path curveToPoint: 
                      scalePoint (points[2], size, rect.origin)
                  controlPoint1: 
                      scalePoint (points[0], size, rect.origin)
                  controlPoint2: 
                      scalePoint (points[1], size, rect.origin)];
            break;
            
        default:
            NSLog (@"unexpected element type in drawRect");
            break;
        }
    }
    
    [path closePath];
    
    return (path);

} // pathInRect


- (NSImage *) imageInRect: (NSRect) rect
{
    NSBezierPath *bpath;
    bpath = [self pathInRect: rect];

    NSImage *image;
    image = [[NSImage alloc] initWithSize: rect.size];

    [image lockFocus];
    [[NSColor blackColor] set];
    [bpath fill];
    [image unlockFocus];

    return ([image autorelease]);

} // imageInRect



- (NSString *) description
{
    return (name);
} // description


- (void) offsetPathByX: (float) x  Y: (float) y
{
    int i;
    for (i = 0; i < itemCount; i++) {

        items[i].points[0].x += x;
        items[i].points[0].y += y;

        if (items[i].type == NSCurveToBezierPathElement) {
            items[i].points[1].x += x;
            items[i].points[1].y += y;
            items[i].points[2].x += x;
            items[i].points[2].y += y;
        }
    }

} // offsetPathByX


- (id) copyWithZone: (NSZone *) zone
{
    [self retain];
    return (self);

} // copyWithZone


- (NSComparisonResult) compare: (BWSymbol *) thing2
{
    return ([name compare: [thing2 name]]);
} // compare


- (void) setPackageName: (NSString *) pname
{
    [packageName release];
    packageName = [pname copy];

} // setPackageName


- (NSString *) packageName
{
    return (packageName);

} // packageName


- (void) setSymbolId: (int) sid
{
    symbolId = sid;
} // setSymbolId


- (int) symbolId
{
    return (symbolId);
} // symbolId


@end // BWSymbol


